package com.lzw.utils;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;


/**
 * Token加密解密工具类，用于Token的生成、加密、解密和验证
 */
public class TokenCryptoUtil {
    // 加密算法
    private static final String ALGORITHM = "AES";
    // 加密密钥(注意：AES要求密钥长度为16、24或32字节)
    private static final String SECRET_KEY = "MySecretKey12345"; // 16字节
    // 存储已失效的Token黑名单，使用ConcurrentHashMap保证线程安全
    private static final Map<String, Long> TOKEN_BLACKLIST = new ConcurrentHashMap<>();

    // 定时清理过期的黑名单记录，避免内存溢出
    private static final ScheduledExecutorService SCHEDULER = Executors.newSingleThreadScheduledExecutor();
    //提供固定的过期时间--默认1天
    private static final long EXPIRE_TIME = 1000 * 3600 * 24;

    //定时任务--定时清理过期的黑名单记录
    static {
        // 初始化定时任务，每小时清理一次过期的黑名单记录
        SCHEDULER.scheduleAtFixedRate(() -> {
            long now = System.currentTimeMillis();
            TOKEN_BLACKLIST.entrySet().removeIf(entry -> entry.getValue() < now);
        }, 1, 1, TimeUnit.HOURS);
    }

    /**
     * 生成Token
     * @param userId 用户ID
     * @param expireTime 过期时间(秒)
     * @return 生成的原始Token字符串
     */
    public static String getToken(String userId, long expireTime) {
        // Token格式: userId:expireTime
        long expire = new Date().getTime() + expireTime;
        return userId + ":" + expire;
    }

    /**
     * 加密Token
     * @param token 原始Token
     * @return 加密后的Token
     * @throws Exception 加密过程中可能抛出的异常
     */
    public static String encryptToken(String token) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), ALGORITHM);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);

        byte[] encrypted = cipher.doFinal(token.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encrypted);
    }

    /**
     * 解密Token
     * @param encryptedToken 加密后的Token
     * @return 解密后的原始Token
     * @throws Exception 解密过程中可能抛出的异常
     */
    public static String decryptToken(String encryptedToken) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), ALGORITHM);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, keySpec);

        byte[] decoded = Base64.getDecoder().decode(encryptedToken);
        byte[] decrypted = cipher.doFinal(decoded);
        return new String(decrypted, StandardCharsets.UTF_8);
    }

    /**
     * 验证Token是否有效
     * @param encryptedToken 加密后的Token
     * @return 验证结果，true为有效，false为无效
     */
    public static boolean validateToken(String encryptedToken) {
        try {
            // 检查是否在黑名单中
            if (TOKEN_BLACKLIST.containsKey(encryptedToken)) {
                return false;
            }

            // 解密Token
            String token = decryptToken(encryptedToken);
            // 解析Token
            String[] parts = token.split(":");
            if (parts.length != 2) {
                return false;
            }

            // 验证过期时间
            long expireTime = Long.parseLong(parts[1]);
            return new Date().getTime() < expireTime;
        } catch (Exception e) {
            // 任何异常都视为Token无效
            return false;
        }
    }

    /**
     * 从Token中获取用户ID
     * @param encryptedToken 加密后的Token
     * @return 用户ID
     * @throws Exception 解析过程中可能抛出的异常
     */
    public static String getUserIdFromToken(String encryptedToken) throws Exception {
        String token = decryptToken(encryptedToken);
        String[] parts = token.split(":");
        if (parts.length != 2) {
            throw new IllegalArgumentException("无效的Token格式");
        }
        return parts[0];
    }

    /**
     * 将Token手动置为过期（加入黑名单）
     * @param encryptedToken 加密后的Token
     */
    public static void addTokenToBlacklist(String encryptedToken) {
        try {
            // 解密获取原Token的过期时间
            String token = decryptToken(encryptedToken);
            String[] parts = token.split(":");
            if (parts.length == 2) {
                long expireTime = Long.parseLong(parts[1]);
                // 计算剩余有效期
                long remainingTime = expireTime - System.currentTimeMillis();
                // 确保保留时间为正数  默认保留1分钟
                long keepTime = Math.max(remainingTime, 60000); // 至少保留1分钟
                TOKEN_BLACKLIST.put(encryptedToken, System.currentTimeMillis() + keepTime);
            }
        } catch (Exception e) {
            // 即使解密失败，也将其加入黑名单
            TOKEN_BLACKLIST.put(encryptedToken, System.currentTimeMillis() + EXPIRE_TIME); // 默认保留1天--这里的时间是根据token有效期保证的
        }
    }

    /**
     * 关闭定时任务（一般在应用关闭时调用）
     */

    public static void shutdown() {
        SCHEDULER.shutdown();
    }

}
